package com.fkjslee;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.client.RestTemplate;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @author fkjslee
 * @date 2018/5/9 at 16:42
 * Copyright (c) 2018 fkjslee. All rights reserved.
 */

class Task {

    private static final Logger log = LoggerFactory.getLogger(DCOPController.class);

    public static final Long DEAD_TIME = 3 * 1000L; // how much time not respond represent the task is dead
    public static final Long CHECK_TIME = 2 * 1000L; // time to check if task is dead

    private static AtomicBoolean lock = new AtomicBoolean(false);

    private static Integer allTaskId = 0;
    Map<String, FieldValueAndType> fields = new HashMap<>();
    String agentName;
    private Integer taskId;
    private String userName;
    private Boolean hasSend;
    private String validateTime;
    private String storeZipName;
    private Long lastTimeResponse;
    private Long startTime;
    private Long needTime;
    private Boolean finished = false;
    private Boolean dead = false;
    public static RestTemplate restTemplate;

    public Task() {
    }

    public Task(String userName, Integer newId, String validateTime, String storeZipName, Long needTime) {
        this.userName = userName;
        this.validateTime = validateTime;
        this.storeZipName = storeZipName;
        this.startTime = System.currentTimeMillis();
        this.needTime = needTime;
        hasSend = false;
        synchronized (lock) {
            if (lock.get()) {
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            lock.set(true);
            if (newId == -1) taskId = ++allTaskId;
            else taskId = newId;
            lock.set(false);
            lock.notify();
        }
        lastTimeResponse = Long.MAX_VALUE;
    }

    public Double getPriority() {
        Double dWaitTime = new Long(Math.max(1L, (System.currentTimeMillis() - startTime))).doubleValue();
        Double dNeedTime = needTime.doubleValue();
        return dWaitTime / (dWaitTime + dNeedTime);
    }

    private synchronized String toFindStation() {
        JSONArray jArraySendParam = new JSONArray();
        for (String key : fields.keySet()) {
            String value = fields.get(key).value;
            JSONObject jObjSendParam = new JSONObject();
            jObjSendParam.put("agentName", agentName);
            jObjSendParam.put("fieldName", key);
            jObjSendParam.put("newValue", value);
            jArraySendParam.add(jObjSendParam);
        }
        log.info("server start to send, taskId = {}, parameters group = {}", taskId, jArraySendParam);
        String msg = jArraySendParam.toString();
        String[] regex = {"00000", "\\{", "}", "\\[", "]"};
        String[] replacement = {"00100", "00200", "00300", "00400", "00500"};
        for (int i = 0; i < regex.length; ++i) {
            msg = msg.replaceAll(regex[i], replacement[i]);
        }
        Integer validate = Double.valueOf(validateTime).intValue();
        msg = "/setParamAndRun?paramGroup=" + msg + "&userName=" + userName + "&zipFileName=" + storeZipName + "&validateTime=" + validate.toString() + "&taskId=" + taskId.toString() + "&needTime=" + needTime.toString();
        hasSend = true;
        JSONObject jObjResult = JSONObject.fromObject(restTemplate.getForEntity(Tool.STATION_NAME + msg, String.class).getBody());
        if (jObjResult != null && jObjResult.getString("code").equals("0")) {
            return "{\"type\":\"success\", \"code\":\"0\"}";
        }
        else {
            log.error("assign task failed, taskId={}, userName={}, paramGroup={}", taskId, userName, jArraySendParam.toString());
            return "{\"type\":\"fail\", \"code\":\"1\", \"reason\":\"不能将任务分配到工作站 : taskId = " + taskId.toString() + "\"}";
        }
    }

    public synchronized String findOneStation() {
        if (!hasSend) {
            log.info("server send task, the task id = {}", taskId);
            HttpThread.setParamAndRun(Tool.LOCAL_HOST_AND_PORT + "/decRemainTaskSize", "userName=" + userName, "Task.java dec remain task size ");
            lastTimeResponse = System.currentTimeMillis();
            hasSend = true;
            String result = toFindStation();
            if (!JSONObject.fromObject(result).getString("code").equals("0")) {
                return result;
            }
        } else if (dead) {
            dead = false;
            log.info("server resend task, the task before id = {}, after id = {}", taskId, allTaskId);
            lastTimeResponse = System.currentTimeMillis();
            synchronized (lock) {
                taskId = ++allTaskId;
            }
            String result = toFindStation();
            if (!JSONObject.fromObject(result).getString("code").equals("0")) {
                return result;
            }
        }
        return "{\"type\":\"success\", \"code\":\"0\"}";
    }

    public Integer getTaskId() {
        return taskId;
    }

    public void setLastTimeResponse(Long lastTimeResponse) {
        this.lastTimeResponse = lastTimeResponse;
    }

    public Boolean getHasSend() {
        return hasSend;
    }

    public void taskFinished() {
        finished = true;
        HttpThread.setParamAndRun(Tool.LOCAL_HOST_AND_PORT + "/incRemainTaskSize", "userName=" + userName, "Task.java dec remain task size ");
    }

    public Boolean getFinished() {
        return finished;
    }

    public Boolean checkDead() {
        if (System.currentTimeMillis() - lastTimeResponse > DEAD_TIME) {
            log.error("task dead , id = {}, lasTime = {}, now = {}", taskId, lastTimeResponse, System.currentTimeMillis());
            dead = true;
            return true;
        }
        return false;
    }

    static class FieldValueAndType {
        String value;
        String type;

        public FieldValueAndType(String value, String type) {
            this.value = value;
            this.type = type;
        }
    }
}


